﻿/* 
 * Copyright 2010 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License")
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Ngilead.Core.Exception;
using log4net;

namespace Ngilead.Core.Replicator
{
    /// <summary>
    /// Replicator for object instance
    /// </summary>
    public class InstanceReplicator : AbstractReplicator
    {
        #region Private

        /// <summary> Logger channel</summary>
        private static ILog logger = LogManager.GetLogger(typeof(InstanceReplicator));

        #endregion

        #region Constructor

        /// <summary>
        /// Constructor
        /// </summary>
        public InstanceReplicator()
        {
        }

        #endregion

        #region Public

        /// <summary>
        /// Replicate the argument object
        /// </summary>
        /// <param name="from">object to replicate</param>
        /// <returns>replicated object</returns>
        public virtual T ReplicateInstance<T>(T from)
        {
            if (logger.IsInfoEnabled)
            {
                logger.Info(String.Format("ReplicateInstance<T>(T from) : where T is {0}", typeof(T).ToString()));
            }

            T to;
            object value;

            try
            {
                // create a new (empty) instance of the same type as the argument
                to = CreateNewInstance(from);
                // put TargetCloned
                PutTargetCloned(from, to);

                //// copy fields
                //FieldInfo[] fields = from.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public);
                //foreach (FieldInfo fi in fields)
                //{
                //    valeur = fi.GetValue(from);

                //    if (valeur != null && valeur.GetType().IsClass)
                //        valeur = beanTransformer.Replicate(valeur);

                //    fi.SetValue(to, valeur);
                //}

                // copy property
                PropertyInfo[] properties = to.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);

                if (logger.IsDebugEnabled)
                {
                    logger.Info(String.Format("PropertyInfo[] : where PropertyInfo[].Length = {0}", properties.Length));
                }

                foreach (PropertyInfo prop in properties)
                {
                    if (logger.IsDebugEnabled)
                    {
                        logger.Info(String.Format("PropertyInfo : where PropertyInfo.Name = {0} and PropertyInfo.PropertyType.Name = {1}", prop.Name, prop.PropertyType.Name));
                    }

                    if (BeanTransformer.ShouldPopulate(prop.Name, from, to))
                    {
                        if (logger.IsDebugEnabled)
                        {
                            logger.Info("BeanTransformer.ShouldPopulate(prop.Name, from, to) = true");
                        }

                        // TODO indexer
                        value = prop.GetValue(from, null);
                        if (value != null)
                        {
                            Type valeurType = value.GetType();
                            if (!valeurType.IsValueType && valeurType != typeof(String))
                                value = BeanTransformer.Replicate(value);
                        }
                        prop.SetValue(to, value, null);
                    }
                }
            }
            catch (System.Exception ex)
            {
                throw new CloneException(ex.Message, ex);
            }

            return to;
        }

        #endregion

        /*----------------------------------------------------------------------------------------*/
        #region Internal methods
        /// <summary>
        /// Create a new instance of the target argument.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="from"></param>
        /// <returns></returns>
        protected virtual T CreateNewInstance<T>(T from)
        {
            if (logger.IsInfoEnabled)
            {
                logger.Info(String.Format("CreateNewInstance<T>(T from) : where T is {0}", typeof(T).ToString()));
            }

            // create a new (empty) instance of the same type as the argument
            Type typeClass = from.GetType();
            return (T)Activator.CreateInstance(typeClass);
        }

        #endregion
        /*----------------------------------------------------------------------------------------*/
    }
}
